package com.iakuil.app.common.core.util;

import cn.hutool.core.util.PageUtil;
import com.github.pagehelper.PageHelper;
import com.iakuil.app.common.core.constant.SysConstant;
import com.iakuil.app.common.core.domain.PageData;
import lombok.experimental.UtilityClass;

import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;

/**
 * 分页工具类
 *
 * @author Kai
 */
@UtilityClass
public class PageUtils extends PageUtil {

    private static final Pattern PATTERN_SINGLE_ORDER_BY = Pattern.compile("^[0-9a-z_]{1,50}$");
    private static final Pattern PATTERN_FULL_ORDER_BY = Pattern.compile("^[0-9a-z_,\\s]{1,50}$");
    private static final Pattern PATTERN_EXP_NONE_COMMA = Pattern.compile("^([\\w_]+[\\+\\-]{1}){1,50}$");
    private static final Pattern PATTERN_EXP_WITH_COMMA = Pattern.compile("^([\\+\\-]+[\\w_]*,)+[\\+\\-][\\w_]{1,50}$");

    /**
     * 启动分页
     *
     * <p>从URL读取orderBy参数，再解析为常规SQL排序语句。
     * <p>支持的格式：
     * <ul>
     * <li>a+b-c+
     * <li>+a,-b,+c
     * </ul>
     * <p>请求示例：/user/list?pageNum=1&pageSize=10&sort=updateTime-createTime+
     */
    public static void startPage() {
        Integer pageNum = ServletUtils.getParameterAsInt(SysConstant.DEFAULT_PAGE_NUM_PARAM);
        Integer pageSize = ServletUtils.getParameterAsInt(SysConstant.DEFAULT_PAGE_SIZE_PARAM);
        String orderBy = parseOrderBy(StringUtils.toUnderlineCase(ServletUtils.getParameter(SysConstant.DEFAULT_SORT_PARAM)));
        startPage(pageNum, pageSize, orderBy);
    }

    /**
     * 启动分页
     *
     * @param pageNum  页码，默认1，最大100
     * @param pageSize 每页记录数目，默认10，最大500
     * @param orderBy  SQL排序语句，比如：update_time desc, create_time asc
     */
    public static void startPage(Integer pageNum, Integer pageSize, String orderBy) {
        // 启动分页排序插件
        Integer num = ObjectUtils.defaultIfNull(pageNum, SysConstant.DEFAULT_PAGE_NUM);
        Integer size = ObjectUtils.defaultIfNull(pageSize, SysConstant.DEFAULT_PAGE_SIZE);

        // 避免深分页
        if (num > SysConstant.MAX_PAGE_NUM) {
            num = SysConstant.MAX_PAGE_NUM;
        }
        // 限制每页记录条数
        if (size > SysConstant.MAX_PAGE_SIZE) {
            size = SysConstant.MAX_PAGE_SIZE;
        }

        if (StringUtils.isNoneBlank(orderBy)) {
            // 防止SQL注入
            if (!PATTERN_FULL_ORDER_BY.matcher(orderBy).matches()) {
                throw new IllegalArgumentException("Invalid sorting character");
            }
            // 此处排序优先级最高，会忽略掉其他排序
            PageHelper.startPage(num, size, orderBy);
        } else {
            PageHelper.startPage(num, size);
        }
    }

    /**
     * 清理分页的线程变量
     */
    public static void clearPage() {
        PageHelper.clearPage();
    }

    /**
     * 逻辑分页
     *
     * <p>默认返回10条，示例：
     * <pre>{@code
     * public R<Foo> getWithLogicalPage(Foo foo) {
     *  return PageUtils.logicalPage(fooService.list(foo));
     * }
     * }</pre>
     *
     * @param entities 实体对象列表
     * @return 逻辑分页后的列表
     */
    public static <T> PageData<T> logicalPage(List<T> entities) {
        int pageNum = ServletUtils.getParameterAsInt(SysConstant.DEFAULT_PAGE_NUM_PARAM, SysConstant.DEFAULT_PAGE_NUM);
        int pageSize = ServletUtils.getParameterAsInt(SysConstant.DEFAULT_PAGE_SIZE_PARAM, SysConstant.DEFAULT_PAGE_SIZE);
        return logicalPage(entities, pageNum, pageSize);
    }

    /**
     * 逻辑分页
     *
     * <p>注意：排序逻辑需要自行实现
     *
     * @param pageNum  页码
     * @param pageSize 每页记录数目
     * @return 逻辑分页后的列表
     */
    public static <T> PageData<T> logicalPage(List<T> entities, int pageNum, int pageSize) {
        if (CollectionUtils.isEmpty(entities) || pageNum < 1 || pageSize < 1) {
            return new PageData<>(0, Collections.emptyList(), 1, pageSize, 0);
        }

        int total = entities.size();
        return new PageData<>(total, subRow(pageNum, pageSize, entities), pageNum, pageSize, calcPages(pageSize, total));
    }

    private static <T> List<T> subRow(int pageNum, int pageSize, List<T> entities) {
        int total = entities.size();
        int fromIndex = (pageNum - 1) * pageSize;
        int toIndex = fromIndex + pageSize;
        if (fromIndex >= total) {
            return Collections.emptyList();
        }
        if (toIndex > total) {
            toIndex = total;
        }
        return entities.subList(fromIndex, toIndex);
    }

    private static int calcPages(int pageSize, int total) {
        return total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
    }

    /**
     * 解析URL分页表达式参数
     *
     * @param sortExp 分页表达式
     * @return 格式化后的分页参数
     */
    public static String parseOrderBy(String sortExp) {
        if (StringUtils.isBlank(sortExp)) {
            return null;
        }

        // 清理所有空白字符
        String orderBy = StringUtils.deleteWhitespace(sortExp);
        // 驼峰转下划线
        orderBy = StringUtils.toUnderlineCase(orderBy);

        if (PATTERN_SINGLE_ORDER_BY.matcher(orderBy).matches()) {
            // 如果全部是字母、数字、下划线，没有逗号，说明是单个排序条件
            return orderBy;
        } else if (PATTERN_EXP_NONE_COMMA.matcher(orderBy).matches()) {
            // 如果是a+b-c+形式的多重排序
            return StringUtils.removeEnd(orderBy.replaceAll("([\\w_])\\+", "$1 asc,").replaceAll("([\\w_])\\-", "$1 desc,"), ",");
        } else if (PATTERN_EXP_WITH_COMMA.matcher(orderBy).matches()) {
            // 如果是+a,-b,+c形式的多重排序
            return StringUtils.removeEnd(orderBy.replaceAll("\\+([\\w_]*)(,|$)", "$1 asc,").replaceAll("\\-([\\w_]*)(,|$)", "$1 desc,"), ",");
        } else {
            throw new IllegalArgumentException("Invalid sorting: " + sortExp + ", expected format: a+b-c+ or +a,-b,+c!");
        }
    }
}
